﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using SwinGame;
using NeuralNetworkPrototype;

namespace SimpleSurvival.src
{
    //-----------------------------------------------//
    //INTERFACE - IController.cs
    //-----------------------------------------------//

    //Used to control entitys
    //As we dont know what kind of controller need
    public interface IController
    {
        //Acts on input
        void GetInput(double dt);
    }

    //-----------------------------------------------//
    //CLASS - ObjectController.cs
    //-----------------------------------------------//

    //Alternative would be to have all controlled objects implement IControlled...
    //Means of linking a controller to an object.
    public abstract class ObjectController<type> : IController
    {
        bool _isLinked = false;
        type _controlledObject = default(type); //will this work?

        public type ControlledObject
        {
            get { return _controlledObject; }
            set { _controlledObject = value; }
        }

        public bool IsLinked
        {
            get { return _isLinked; }
            set { _isLinked = value; }
        }

        public virtual void LinkToObject(type t)
        {
            //Give entity objects a reference to the controller
            IEntity ent = t as IEntity;
            if (ent != null)
            {
                ((IEntity)t).Controller = this;
            }

            ControlledObject = t;
            IsLinked = true;
        }

        //Other children will extend
        public abstract void GetInput(double dt);
    }

    //-----------------------------------------------//
    //CLASS - AIUnitController.cs
    //-----------------------------------------------//

    //Controls Unit AI
    public class AIUnitController : ObjectController<IUnit>
    {
        //-----------------------------------------------//
        //Extension - NeuralNetwork Integration
        #region NeuralNetwork mod - ---------------------//
        INeuralNet _neuralNet = null;
        INeuralNetMutator _neuralMutator = null;

        public INeuralNet NeuralNet { get { return _neuralNet; } }
        public INeuralNetMutator Mutator { get { return _neuralMutator; } } 

        public AIUnitController(INeuralNet nn, INeuralNetMutator mtr)
        {
            _neuralMutator = mtr;
            _neuralNet = nn;
            _neuralMutator.Nuke(-10, 3, 1, _neuralNet);
        }
        #endregion

        public override void LinkToObject(IUnit t)
        {
            base.LinkToObject(t);
            t.Team = UnitTeam.ENEMY_UNIT;   //Set to enemy team
        }
        public override void GetInput(double dt)
        {
            const int PlayerId = 0;

            //Needs a better way to reference player - Look at that chain below...

            if (IsLinked)
            {
                //Moves the unit in the direction of the player
                Vector dv = CORE.GetDirectionalVector(
                    ControlledObject.Entity.Map.Units[PlayerId].Entity.CenterPoint, //too long xD
                    ControlledObject.Entity.CenterPoint);

                float dx = ControlledObject.Entity.Map.Units[PlayerId].Entity.CenterPoint.X - ControlledObject.Entity.CenterPoint.X;
                float dy = ControlledObject.Entity.Map.Units[PlayerId].Entity.CenterPoint.Y - ControlledObject.Entity.CenterPoint.Y;
                
                //-----------------------------------------------//
                //Extension - NeuralNetwork Integration
                #region NeuralNetwork mod - ---------------------//
                
                List<float> nnout = _neuralNet.SendInputGetOutput(new float[2] { dx, dy }.ToList());
                if (!(dv.X == 0 && dv.Y == 0))
                {
                    ControlledObject.Entity.Move(new Vector { X = nnout[0], Y = nnout[1]}, dt);
                }

                //Console.WriteLine(dv.X + " , " + dv.Y + " -> " + (nnout[0] - 1f) + " , " + (nnout[1] - 1f));
                #endregion

            }
            else
            {
                throw new Exception("ai controller not linked");
            }

        }
    }

    //-----------------------------------------------//
    //CLASS - UnitControllers.cs
    //-----------------------------------------------//

    //Controls the players unit
    public class UnitControllers : ObjectController<IUnit>
    {
        public const int MOVE_THRESHOLD = 16;

        ICamera _cam = null;                        //To move the camera
        EntityFactory _entityFactory = null;        //To create damage projectiles

        public override void LinkToObject(IUnit u)
        {
            base.LinkToObject(u);
            u.Team = UnitTeam.PLAYER_UNIT;          //Set to player team
        }

        public UnitControllers(ICamera cam, EntityFactory ef) { _cam = cam; _entityFactory = ef; }

        public override void GetInput(double dt)
        {
            if (IsLinked)
            {
                //Move in mouse click direction
                if (Input.MouseDown(MouseButton.LeftButton) || Input.MouseClicked(MouseButton.LeftButton))
                {
                    Vector mouseV = Input.MousePositionAsVector();
                    Vector uPos = _cam.GetRelativePosition(
                        new Vector
                        {
                            X = ControlledObject.Entity.CenterPoint.X,
                            Y = ControlledObject.Entity.CenterPoint.Y
                        });

                    mouseV.X -= uPos.X;
                    mouseV.Y -= uPos.Y;

                    float d2 = mouseV.X * mouseV.X + mouseV.Y * mouseV.Y;
                    
                    if (d2 > MOVE_THRESHOLD * MOVE_THRESHOLD)     //threshold
                    {

                        float d = CORE.Sqrt(d2);
                        mouseV.X /= d;
                        mouseV.Y /= d;
                        ControlledObject.Entity.Move(mouseV, dt);
                    }
                    else
                    {
                        //If click within range (on player) - Create Damage Projectile
                        if (Input.MouseClicked(MouseButton.LeftButton))
                        {
                            float d = CORE.Sqrt(d2);
                            mouseV.X /= d;
                            mouseV.Y /= d;
                            ControlledObject.Attack(mouseV,_entityFactory);
                        }
                    }
                }
                else
                {
                    //Move unit with WASD
                    Point kbOffset = KeyboardInput.GetKeyboardOffset(KeyCode.vk_a,
                        KeyCode.vk_d, KeyCode.vk_w, KeyCode.vk_s);

                    //does it have to move?
                    if (!kbOffset.IsEmpty)
                    ControlledObject.Entity.Move(new Vector
                    {
                        X = kbOffset.X,
                        Y = kbOffset.Y
                    }, dt);
                }
            }
            else
            {
                throw new Exception("player controller not linked");
            }
        }
    }
}
